package com.thoughtworks.bytecode2class.instruction;

import java.util.List;

public class InstructionFactory {

    public static Instruction create(List<Byte> bytes, int offset) {
        byte opCode = bytes.get(offset);
        OpCode thisOpCode = OpCode.findBy(opCode);
        if (thisOpCode.hasParam()) {
            return createOpCodeFrom(thisOpCode, bytes, offset);
        } else {
            return new Instruction(thisOpCode, bytes, offset);
        }
    }

    private static Instruction createOpCodeFrom(OpCode opCode, List<Byte> bytes, int offset) {
        switch (opCode.getOpCode()) {
            case (byte) 0x15:  // iload
            case (byte) 0x16:  // lload
            case (byte) 0x17:  // fload
            case (byte) 0x18:  // dload
            case (byte) 0x19:  // aload
            case (byte) 0x36:  // istore
            case (byte) 0x37:  // lstore
            case (byte) 0x38:  // fstore
            case (byte) 0x39:  // dstore
            case (byte) 0x3a:  // astore
            case (byte) 0xa9:  // ret
                return new LocalIndexInstruction(opCode, bytes, offset);
            case (byte) 0x13: // ldc_w
            case (byte) 0x14: // ldc2_w
            case (byte) 0xb2: // getstatic
            case (byte) 0xbb: // new 
            case (byte) 0xb3: // putstatic
            case (byte) 0xb4: // getfield
            case (byte) 0xb5: // putfield
            case (byte) 0xb6: // invokevirtual
            case (byte) 0xb7: // invokespecial
            case (byte) 0xb8: // invokestatic
            case (byte) 0xbd: // anewarray
            case (byte) 0xc0: // checkcast
            case (byte) 0xc1: // instanceof
                return new ConstantPoolIndexInstruction(opCode, bytes, offset);
            case (byte) 0x99: // ifeq
            case (byte) 0x9a: // ifne
            case (byte) 0x9b: // iflt
            case (byte) 0x9c: // ifge
            case (byte) 0x9d: // ifgt
            case (byte) 0x9e: // ifle
            case (byte) 0x9f: // if_icmpeq
            case (byte) 0xa0: // if_icmpne
            case (byte) 0xa1: // if_icmplt
            case (byte) 0xa2: // if_icmpge
            case (byte) 0xa3: // if_icmpgt
            case (byte) 0xa4: // if_icmple
            case (byte) 0xa5: // if_acmpeq
            case (byte) 0xa6: // if_acmpne
            case (byte) 0xa7: // goto
            case (byte) 0xa8: // jsr
            case (byte) 0xc6: // ifnull
            case (byte) 0xc7: // ifnonnull
                return new DoubleByteInstruction(opCode, bytes, offset);
            case (byte) 0xc8: // goto_w
            case (byte) 0xc9: // jsr_w
                return new FourByteInstruction(opCode, bytes, offset);

            //special cases
            case (byte) 0xb9:
             return new INVOKEINTERFACE(bytes, offset);
            case (byte) 0xab:
             return new LOOKUPSWITCH(bytes, offset);
            case (byte) 0xbc:
             return new NEWARRAY(bytes, offset);
            case (byte) 0xaa:
             return new TABLESWITCH(bytes, offset);
            case (byte) 0xc4:
             return new WIDE(bytes, offset);
            case (byte) 0x84:
             return new IINC(bytes, offset);
            case (byte) 0x12:
             return new LDC(bytes, offset);
            case (byte) 0x10:
             return new BIPUSH(bytes, offset);
        }
        throw new RuntimeException("Unknown Instruction " + opCode);
    }

}
